﻿using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.Filters;
using PMS.Core.Configuration.CommonEnumConfigs;
using PMS.Core.Utils.Authentication;
using PMS.Data.Entities.Customers;
using PMS.Services.EntityServices.Customers;
using PMS.Services.SharedKernel;
using PMS.Services.Utils;
using System;
using System.Linq;

namespace PMS.Web.Framework.Mvc.Filters
{
    /// <summary>
    /// DavidLee Customer Authorization with For Controller
    /// </summary>
    public sealed class AuthorizeCustomerAttribute : TypeFilterAttribute
    {
        #region Fields

        private readonly CustomerAuthorizeType _ignoreFilter;
        #endregion

        #region Ctor

        /// <summary>
        /// Create instance of the filter attribute
        /// </summary>
        /// <param name="ignore">Whether to ignore the execution of filter actions</param>
        public AuthorizeCustomerAttribute(CustomerAuthorizeType ignore = CustomerAuthorizeType.Ignore) 
            : base(typeof(AuthorizeCustomerFilter))
        {
            _ignoreFilter = ignore;
            Arguments = new object[] { ignore };
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets a value indicating whether to ignore the execution of filter actions
        /// </summary>
        public CustomerAuthorizeType IgnoreFilter => _ignoreFilter;

        #endregion

        #region Nested filter

        /// <summary>
        /// Represents a filter that confirms access to the admin panel
        /// </summary>
        private class AuthorizeCustomerFilter : IAuthorizationFilter
        {
            #region Fields

            private readonly CustomerAuthorizeType _ignoreFilter;
            private readonly IWebHelper _webHelper;
            private readonly IWorkContext _workContext;
            private readonly ICustomerService _customerService;
            #endregion

            #region Ctor

            public AuthorizeCustomerFilter(CustomerAuthorizeType ignoreFilter,
                IWebHelper webHelper,
                IWorkContext workContext,
                ICustomerService customerService)
            {
                _webHelper = webHelper;
                _workContext = workContext;
                _ignoreFilter = ignoreFilter;
                _customerService = customerService;
            }

            #endregion

            #region Utils
            private bool AuthorizeCusomterInRole(CustomerAuthorizeType ignoreType)
            {
                Customer loginUser = _workContext.CurrentCustomer;
                return ignoreType switch
                {
                    CustomerAuthorizeType.Ignore => true,
                    CustomerAuthorizeType.Guests => _customerService.IsRegistered(loginUser),
                    CustomerAuthorizeType.Admin => loginUser?.IsAdminUser() ?? loginUser?.IsAdministrator() ?? false,
                    CustomerAuthorizeType.Administrator => loginUser?.IsAdministrator() ?? false,
                    CustomerAuthorizeType.BuildInTaskUser => loginUser?.IsBackgroundBuidInTaskUser() ?? false,
                    _ => false
                };
            }

            private void HandleAuthorizeResult(bool isPass, AuthorizationFilterContext filterContext)
            {
                if(!isPass)
                {
                    if (_webHelper.IsAjaxRequest(filterContext.HttpContext.Request))
                    {
                        object responseData = new
                        {
                            error = "您没有操作权限",
                            error_description = "您当前访问页面需要操作授权,可能没有登录或没有操作权限"
                        };
                        filterContext.Result = new JsonResult(responseData)
                        {
                            StatusCode = StatusCodes.Status401Unauthorized,
                            ContentType = "application/json"
                        };
                    }
                    else
                    {
                        filterContext.Result = new RedirectResult(AuthenticationDefaultConfigs.AccessDeniedPath, false);
                    }
                }
            }
            #endregion

            #region Methods

            /// <summary>
            /// Called early in the filter pipeline to confirm request is authorized
            /// </summary>
            /// <param name="filterContext">Authorization filter context</param>
            public void OnAuthorization(AuthorizationFilterContext filterContext)
            {
                if (filterContext == null)
                    throw new ArgumentNullException(nameof(filterContext));
                Customer loginUser = null;
                //check whether this filter has been overridden for the action
                var actionFilter = filterContext.ActionDescriptor.FilterDescriptors
                    .Where(filterDescriptor => filterDescriptor.Scope == FilterScope.Action)
                    .Select(filterDescriptor => filterDescriptor.Filter).OfType<AuthorizeCustomerAttribute>().FirstOrDefault();

                if (actionFilter?.IgnoreFilter.GetType() == typeof(CustomerAuthorizeType))
                {
                    var filterArg = actionFilter?.IgnoreFilter ?? _ignoreFilter;
                    if (filterArg == CustomerAuthorizeType.Ignore)
                    {
                        var descriptor = filterContext?.ActionDescriptor as ControllerActionDescriptor;
                        string actionName = descriptor?.ActionName ?? string.Empty;
                        string ctrlName = descriptor?.ControllerName ?? string.Empty;
                        if (!string.IsNullOrEmpty(actionName) && !string.IsNullOrEmpty(ctrlName)
                            && actionName.Equals("Login", StringComparison.InvariantCultureIgnoreCase)
                            && ctrlName.Equals("BackendLogin", StringComparison.InvariantCultureIgnoreCase))
                        {
                            loginUser = _workContext.CurrentCustomer;

                            if (loginUser?.IsAdminUser() ?? loginUser?.IsAdministrator() ?? false)
                                filterContext.Result = new RedirectToActionResult("Home", "BackendHome", null, false);
                        }
                        return;
                    }
                    HandleAuthorizeResult(AuthorizeCusomterInRole(filterArg), filterContext);
                    //优先执行Controller中Action方法中的权限授权，如方法中无授权验证则验证Controller类上的授权
                    return;
                }
                   
                //there is AuthorizeCustomerFilter, so check access
                if (filterContext.Filters.Any(filter => filter is AuthorizeCustomerFilter))
                {
                    HandleAuthorizeResult(AuthorizeCusomterInRole(_ignoreFilter), filterContext);
                }
            }

            #endregion
        }

        #endregion
    }
}
